﻿//-------------------------------------------------------------------
// VOImage implementation
//-------------------------------------------------------------------
//
// Copyright ?000 Virtual Office Systems Incorporated
// All Rights Reserved
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This code can be compiled, modified and distributed freely, providing
// that this copyright information remains intact in the distribution.
//
// This code may be compiled in original or modified form in any private
// or commercial application.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage, in any form, caused
// by this code. Use it at your own risk.
//-------------------------------------------------------------------

#include "stdafx.h"

#include "VOImage.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

#ifdef _WIN32_WCE
#pragma comment(lib, "imgdecmp.lib")
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

int		CVOImage::g_iScale		= 100;
int		CVOImage::g_iMaxWidth	= 10000;
int		CVOImage::g_iMaxHeight	= 10000;
BOOL	CVOImage::g_bStretchBlt	= FALSE;

CVOImage::CVOImage()
		: m_hDC(NULL),
		m_hBitmap(NULL),
		m_dwWidth(0),
		m_dwHeight(0),
		m_hModuleResource(NULL),
		m_dwResourceID(0)
{}

CVOImage::~CVOImage()
{
	::DeleteDC(m_hDC);
	::DeleteObject(m_hBitmap);
}

BOOL CVOImage::Load(HDC hdc, LPCTSTR pcszFileName)
{
	if ( NULL != m_hBitmap )
	{
		if ( GetFileName().GetLength() && GetFileName() == pcszFileName )
		{
			return TRUE;	// Already Loaded
		}

		::DeleteObject(m_hBitmap);
	}

	if ( NULL == m_hDC )
	{
		m_hDC = ::CreateCompatibleDC(hdc);

		const	int	iHorzres	= ::GetDeviceCaps(hdc, HORZRES);
		const	int	iVertres	= ::GetDeviceCaps(hdc, VERTRES);
		HBITMAP	hbitmap			= ::CreateCompatibleBitmap(hdc, iHorzres, iVertres);

		::SelectObject(m_hDC, hbitmap);
	}

	BYTE	szBuffer[1024]	= {0};
	HANDLE	hFile			= INVALID_HANDLE_VALUE;

#ifdef _WIN32_WCE
	HRESULT hr;

	DecompressImageInfo	dii;
#endif

	hFile = ::CreateFile(pcszFileName,
						 GENERIC_READ,
						 FILE_SHARE_READ,
						 NULL,
						 OPEN_EXISTING,
						 FILE_ATTRIBUTE_NORMAL,
						 NULL);

	if ( hFile == INVALID_HANDLE_VALUE )
	{
		return FALSE;
	}

#ifdef _WIN32_WCE
	// Fill in the 'DecompressImageInfo' structure
	dii.dwSize = sizeof( DecompressImageInfo );		// Size of this structure
	dii.pbBuffer = szBuffer;						// Pointer to the buffer to use for data
	dii.dwBufferMax = 1024;							// Size of the buffer
	dii.dwBufferCurrent = 0;						// The amount of data which is current in the buffer
	dii.phBM = &m_hBitmap;							// Pointer to the bitmap returned (can be NULL)
	dii.ppImageRender = NULL;						// Pointer to an IImageRender object (can be NULL)
	dii.iBitDepth = ::GetDeviceCaps(hdc, BITSPIXEL);// Bit depth of the output image
	dii.lParam = ( LPARAM ) hFile;					// User parameter for callback functions
	dii.hdc = m_hDC;								// HDC to use for retrieving palettes
	dii.iScale = g_iScale;							// Scale factor (1 - 100)
	dii.iMaxWidth = g_iMaxWidth;					// Maximum width of the output image
	dii.iMaxHeight = g_iMaxHeight;					// Maximum height of the output image
	dii.pfnGetData = GetImageData;					// Callback function to get image data
	dii.pfnImageProgress = ImageProgress;			// Callback function to notify caller of progress decoding the image
	dii.crTransparentOverride = ( UINT ) - 1;		// If this color is not (UINT)-1, it will override the
	// transparent color in the image with this color. (GIF ONLY)

	// Process and decompress the image data
	hr = ::DecompressImageIndirect( &dii );
#endif

	// Clean up
	::CloseHandle(hFile);

	HBITMAP hbitmapOld = (HBITMAP)::SelectObject(m_hDC, m_hBitmap);

	::DeleteObject(hbitmapOld);

	BITMAP	bmp;

	::GetObject(m_hBitmap, sizeof(BITMAP), &bmp);

	m_dwWidth			= bmp.bmWidth;
	m_dwHeight			= bmp.bmHeight;
	m_wPlanes			= bmp.bmPlanes;
	m_wBitsPixel		= bmp.bmBitsPixel;

	m_strFileName		= pcszFileName;
	m_dwResourceID		= 0;
	m_hModuleResource	= 0;

	return TRUE;
}

HBITMAP CVOImage::Copy() const
{
	BITMAP	bm, bmNew;
	HBITMAP hNew;

	::GetObject(m_hBitmap, sizeof(BITMAP), &bm);

	HDC hdc = ::CreateCompatibleDC(m_hDC);

	int cx = bm.bmWidth;
	int cy = bm.bmHeight;

	hNew = ::CreateCompatibleBitmap(m_hDC, cx, cy);
	::SelectObject(hdc, hNew);

	RECT rect;

	rect.left	= 0;
	rect.top	= 0;
	rect.right	= cx;
	rect.bottom	= cy;

	::FillRect(hdc, &rect, (HBRUSH)::GetStockObject(WHITE_BRUSH));

	if ( Draw(hdc, 0, 0) )
	{
		HBITMAP hPrev = (HBITMAP) ::GetObject(hNew, sizeof(BITMAP), &bmNew);

		::SelectObject(hdc, hPrev);
	}

	::DeleteDC(hdc);

	return hNew;
}

BOOL CVOImage::Draw(HDC hdc, int x, int y, int cx, int cy) const
{
	BITMAP	bmp;

	g_bStretchBlt = !(cx == -1 && cy == -1);

	::GetObject(m_hBitmap, sizeof(BITMAP), &bmp);

	BOOL bResult = FALSE;

	if ( g_bStretchBlt )	// Stretch to fit
	{
		bResult = ::StretchBlt(hdc,
							   x , y, cx, cy,
							   m_hDC,
							   0, 0,
							   bmp.bmWidth, bmp.bmHeight,
							   SRCCOPY);
	}
	else
	{
		bResult = ::BitBlt(hdc,
						   x, y,
						   bmp.bmWidth, bmp.bmHeight,
						   m_hDC,
						   0, 0,
						   SRCCOPY);
	}

	return bResult;
}

BOOL CVOImage::DrawTranslucent(HDC hdc,
							   int x,
							   int y,
							   int nPercent,
							   int cx,
							   int cy,
							   COLORREF crTransparent) const
{
	BITMAP	bmpBG, bmpImage;
	HDC		hdcBG		= ::CreateCompatibleDC(hdc);
	HDC		hdcImage	= ::CreateCompatibleDC(hdc);

	if ( -1 == cx )
	{
		cx = GetWidth();
	}

	if ( -1 == cy )
	{
		cy = GetHeight();
	}

	BITMAPINFO i;

	::memset( &i.bmiHeader, 0, sizeof(BITMAPINFOHEADER) );
	i.bmiHeader.biWidth		= cx;
	i.bmiHeader.biHeight	= cy;
	i.bmiHeader.biPlanes	= 1;
	i.bmiHeader.biBitCount	= 24;
	i.bmiHeader.biSize		= sizeof(BITMAPINFOHEADER);

	PBYTE	pBitsBG		= NULL;
	HBITMAP	hbitmapBG	= ::CreateDIBSection(hdc,
										   &i,
										   DIB_RGB_COLORS,
										   (PVOID*) & pBitsBG,
										   NULL,
										   0);

	if ( NULL == hbitmapBG )
	{
		/*DWORD dwError = */::GetLastError();

		DebugBreak();

		return FALSE;
	}

	// Copy the background into the 24bit Background DIB
	HBITMAP hbitmapOld = (HBITMAP)::SelectObject(hdcBG, hbitmapBG);
	::BitBlt(hdcBG, 0, 0, cx, cy, hdc, x, y, SRCCOPY);
	::SelectObject(hdcBG, hbitmapOld);


	// Create a matching 24bit DIB Section to hold the image data
	PBYTE	pBitsImage		= NULL;
	HBITMAP	hbitmapImage	= ::CreateDIBSection(hdc,
						   &i,
						   DIB_RGB_COLORS,
						   (PVOID*) & pBitsImage,
						   NULL,
						   0);

	if ( NULL == hbitmapImage )
	{
		/*DWORD dwError = */::GetLastError();

		::DebugBreak();

		return FALSE;
	}

	// Copy the bitmap into the 24bit DIB
	hbitmapOld = (HBITMAP)SelectObject(hdcImage, hbitmapImage);

	if ( RGB(192, 192, 192) == crTransparent )
	{
		::StretchBlt(hdcImage,
					 0, 0, cx, cy,
					 m_hDC,
					 0, 0, GetWidth(), GetHeight(),
					 SRCCOPY);
	}
	else
	{
#ifdef _WIN32_WCE
		::BitBlt(hdcImage, 0, 0, cx, cy, hdc, x, y, SRCCOPY);

		::TransparentImage(hdcImage,
						   0,
						   0,
						   cx,
						   cy,
						   m_hDC,
						   0,
						   0,
						   GetWidth(),
						   GetHeight(),
						   crTransparent);
#else
		::StretchBlt(hdcImage,
					 0, 0, cx, cy,
					 m_hDC,
					 0, 0, GetWidth(), GetHeight(),
					 SRCCOPY);
#endif
	}

	::SelectObject(hdcImage, hbitmapOld);

	::GetObject(hbitmapImage, sizeof(BITMAP), &bmpImage);
	::GetObject(hbitmapBG, sizeof(BITMAP), &bmpBG);

	PBYTE	pPixelBG	= NULL;
	PBYTE	pPixelImage	= NULL;
	int		r, g, b;
	int		nPercentBG	= 100 - nPercent;

	for ( int yOffset = 0; yOffset < cy; ++yOffset )
	{
		for ( int xOffset = 0; xOffset < cx; ++xOffset )
		{
			pPixelBG = (PBYTE)bmpBG.bmBits + (cx * yOffset + xOffset) * 3;
			pPixelImage = (PBYTE)bmpImage.bmBits + (cx * yOffset + xOffset) * 3;

			r = ((pPixelImage[2] * nPercent / 100) + ((int)pPixelBG[2] * nPercentBG / 100));
			g = ((pPixelImage[1] * nPercent / 100) + ((int)pPixelBG[1] * nPercentBG / 100));
			b = ((pPixelImage[0] * nPercent / 100) + ((int)pPixelBG[0] * nPercentBG / 100));

			pPixelBG[2] = (BYTE)r;
			pPixelBG[1] = (BYTE)g;
			pPixelBG[0] = (BYTE)b;
		}
	}

	hbitmapOld = (HBITMAP)(::SelectObject(hdcBG, hbitmapBG));

	::BitBlt(hdc, x, y, cx, cy, hdcBG, 0, 0, SRCCOPY );

	::SelectObject(hdcBG, hbitmapOld);

	::DeleteObject(hbitmapImage);
	::DeleteObject(hbitmapBG);

	::DeleteDC(hdcImage);
	::DeleteDC(hdcBG);

	return TRUE;
}

BOOL CVOImage::DrawTransparent(HDC hdc,
							   int x,
							   int y,
							   int cx,
							   int cy,
							   COLORREF crTransparent)
{
	if ( -1 == cx )
	{
		cx = GetWidth();
	}
	if ( -1 == cy )
	{
		cy = GetHeight();
	}

	if (crTransparent == RGB(192, 192, 192))
	{
		crTransparent = GetPixel(0, 0);	// If none of the above conditions are met, use the pixel at 0,0 (Uppler Left)
	}

#ifdef _WIN32_WCE
	return ::TransparentImage(hdc,
							  x,
							  y,
							  cx,
							  cy,
							  m_hDC,
							  0,
							  0,
							  GetWidth(),
							  GetHeight(),
							  crTransparent);
#else
	return FALSE;
#endif
}

DWORD CVOImage::GetHeight() const
{
	return m_dwHeight;
}

DWORD CVOImage::GetWidth() const
{
	return m_dwWidth;
}

WORD CVOImage::GetPlanes() const
{
	return m_wPlanes;
}

WORD CVOImage::GetBitsPixel() const
{
	return m_wBitsPixel;
}

BOOL CVOImage::SetBitmap(HDC hdc,
						 DWORD dwResourceID,
						 LPCTSTR pcszClass,
						 HMODULE hModule)
{
	if ( NULL != m_hBitmap )
	{
		if (m_hModuleResource == hModule && m_dwResourceID == dwResourceID)
		{
			return TRUE;	// Already loaded
		}

		::DeleteObject(m_hBitmap);
	}

	if ( NULL == m_hDC )
	{
		m_hDC = ::CreateCompatibleDC(hdc);

		HBITMAP	hbitmap = ::CreateCompatibleBitmap(hdc,
						  ::GetDeviceCaps(hdc, HORZRES),
						  ::GetDeviceCaps(hdc, VERTRES));

		::SelectObject(m_hDC, hbitmap);
	}

	BYTE    szBuffer[1024] = {0};

#ifdef _WIN32_WCE
	HRESULT hr;

	DecompressImageInfo	dii;
#endif

	if (hModule == 0)
	{
		hModule = ::GetModuleHandle(NULL);
	}

	CVOResource	res(hModule, dwResourceID, pcszClass);

	if ( !res.IsLoaded() )
	{
		return FALSE;
	}

	res.SetUserData(0);	// Use this for the current resource offset

#ifdef _WIN32_WCE
	// Fill in the 'DecompressImageInfo' structure
	dii.dwSize = sizeof( DecompressImageInfo );		// Size of this structure
	dii.pbBuffer = szBuffer;						// Pointer to the buffer to use for data
	dii.dwBufferMax = 1024;							// Size of the buffer
	dii.dwBufferCurrent = 0;						// The amount of data which is current in the buffer
	dii.phBM = &m_hBitmap;							// Pointer to the bitmap returned (can be NULL)
	dii.ppImageRender = NULL;						// Pointer to an IImageRender object (can be NULL)
	dii.iBitDepth = GetDeviceCaps(hdc, BITSPIXEL);	// Bit depth of the output image
	dii.lParam = ( LPARAM ) & res;					// User parameter for callback functions
	dii.hdc = m_hDC;								// HDC to use for retrieving palettes
	dii.iScale = g_iScale;							// Scale factor (1 - 100)
	dii.iMaxWidth = g_iMaxWidth;					// Maximum width of the output image
	dii.iMaxHeight = g_iMaxHeight;					// Maximum height of the output image
	dii.pfnGetData = GetImageResourceData;			// Callback function to get image data
	dii.pfnImageProgress = ImageProgress;			// Callback function to notify caller of progress decoding the image
	dii.crTransparentOverride = ( UINT ) - 1;		// If this color is not (UINT)-1, it will override the
	// transparent color in the image with this color. (GIF ONLY)
	// Process and decompress the image data
	hr = ::DecompressImageIndirect( &dii );
#endif

	HBITMAP hbitmapOld = (HBITMAP)::SelectObject(m_hDC, m_hBitmap);

	::DeleteObject(hbitmapOld);

	BITMAP	bmp;

	::GetObject(m_hBitmap, sizeof(BITMAP), &bmp);

	m_dwWidth		= bmp.bmWidth;
	m_dwHeight		= bmp.bmHeight;
	m_wPlanes		= bmp.bmPlanes;
	m_wBitsPixel	= bmp.bmBitsPixel;

	m_strFileName.Empty();
	m_hModuleResource	= hModule;
	m_dwResourceID		= dwResourceID;

	return TRUE;
}

DWORD CALLBACK CVOImage::GetImageData(LPSTR szBuffer,
									  DWORD dwBufferMax,
									  LPARAM lParam)
{
	DWORD dwNumberOfBytesRead;

	if ( (HANDLE)lParam == INVALID_HANDLE_VALUE )
	{
		return 0;
	}

	::ReadFile((HANDLE)lParam,
			   szBuffer,
			   dwBufferMax,
			   &dwNumberOfBytesRead,
			   NULL);

	// Return number of bytes read
	return dwNumberOfBytesRead;
}

DWORD CALLBACK CVOImage::GetImageResourceData(LPSTR szBuffer,
		DWORD dwBufferMax,
		LPARAM lParam)
{
	DWORD			dwNumberOfBytesToRead	= dwBufferMax;
	CVOResource*	pRes					= (CVOResource*)lParam;

	if ( NULL == pRes )
	{
		return 0;
	}

	DWORD dwResourceOffset = pRes->GetUserData();

	if ( dwResourceOffset + dwNumberOfBytesToRead > pRes->GetSize() )
	{
		dwNumberOfBytesToRead = pRes->GetSize() - dwResourceOffset;
	}

	::memmove(szBuffer,
			  pRes->GetData() + dwResourceOffset,
			  dwNumberOfBytesToRead);

	pRes->SetUserData(dwResourceOffset + dwNumberOfBytesToRead);

	return dwNumberOfBytesToRead;	// return amount read
}

#ifdef _WIN32_WCE
void CALLBACK CVOImage::ImageProgress(IImageRender* /*pRender*/,
									  BOOL bComplete,
									  LPARAM /*lParam*/)
{
	if ( bComplete )
	{
		;// (Optional) add code here for completion processing
	}
}
#endif

BOOL CVOImage::IsLoaded() const
{
	return ( NULL != m_hBitmap );
}

CVOResource::CVOResource(HMODULE hModule,
						 DWORD dwResourceID,
						 LPCTSTR pcszClass)
{
	m_dwSize	= 0;
	m_hGlobal	= 0;
	m_pData		= 0;

	m_hrsrc		= ::FindResource(hModule, (LPCTSTR)dwResourceID, pcszClass);

	if ( NULL == m_hrsrc )
	{
		return;
	}

	m_dwSize	= ::SizeofResource(hModule, m_hrsrc);
	m_hGlobal	= ::LoadResource(hModule, m_hrsrc);
	m_pData		= (PBYTE)(::LockResource(m_hGlobal));
}

CVOResource::~CVOResource()
{
	if ( NULL != m_hGlobal )
	{
		::DeleteObject(m_hGlobal);
	}
}

DWORD CVOResource::GetSize()
{
	return m_dwSize;
}

PBYTE CVOResource::GetData()
{
	return m_pData;
}

void CVOResource::SetUserData(DWORD dwValue)
{
	m_dwUser = dwValue;
}

DWORD CVOResource::GetUserData()
{
	return m_dwUser;
}

BOOL CVOResource::IsLoaded()
{
	return ( NULL != m_pData );
}

COLORREF CVOImage::GetPixel(int x, int y)
{
	if ( NULL == m_hDC )
	{
		return RGB(0, 0, 0);
	}

	return ::GetPixel(m_hDC, x, y);
}

BOOL CVOImage::SetCanvasSize(int x,
							 int y,
							 COLORREF rgbBackground,
							 BOOL bfHCenter,
							 BOOL bfVCenter)
{
	if ( RGB(1, 1, 1) == rgbBackground )
	{
		rgbBackground = GetPixel(0, 0);
	}

	if ( x < (int)GetWidth() || y < (int)GetHeight() )
	{
		return FALSE;	// Cropping may be implemented at some point in the future
	}

	int nOffsetX = 0;
	int nOffsetY = 0;

	if ( bfHCenter )
	{
		nOffsetX = (x - GetWidth()) / 2;
	}

	if ( bfVCenter )
	{
		nOffsetY = (y - GetHeight()) / 2;
	}

	BITMAP	bm, bmNew;

	::GetObject(m_hBitmap, sizeof(BITMAP), &bm);

	HDC hdc		= ::CreateCompatibleDC(m_hDC);

	HBITMAP hbitmapNew	= ::CreateCompatibleBitmap(m_hDC, x, y);
	::SelectObject(hdc, hbitmapNew);

	RECT rect;

	rect.left = 0;
	rect.top = 0;
	rect.right = x;
	rect.bottom = y;

	::FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));

	if ( !::BitBlt(hdc,
				   nOffsetX,
				   nOffsetY,
				   GetWidth(),
				   GetHeight(),
				   m_hDC,
				   0,
				   0,
				   SRCCOPY) )
	{
		::DeleteDC(hdc);
		::DeleteObject(hbitmapNew);

		return FALSE;
	}

	HBITMAP hPrev = (HBITMAP)::GetObject(hbitmapNew, sizeof(BITMAP), &bmNew);

	::SelectObject(hdc, hPrev);

	::DeleteDC(m_hDC);
	::DeleteObject(m_hBitmap);

	m_hDC		= hdc;
	m_hBitmap	= hbitmapNew;

	return TRUE;
}

CVOImage::operator HBITMAP()
{
	return m_hBitmap;
}

const CVOString& CVOImage::GetFileName() const
{
	return m_strFileName;
}
